/*
 * Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file
 * for details. All rights reserved. Use of this source code is governed by a
 * BSD-style license that can be found in the LICENSE file.
 */
/** 
 * @description A canvas globalCompositeOperation test with alpha compositing
 * on text
 */
import "dart:html";
import "dart:math" as Math;
import "../../testcommon.dart";
import "../../../Utils/async_utils.dart";

class DrawPolicy {
  var drawSource;
  var drawDestination;
  var drawComposition;
  var name;
  DrawPolicy({drawSource,drawDestination,drawComposition,name}) {
    this.drawSource = drawSource;
    this.drawDestination = drawDestination;
    this.drawComposition = drawComposition;
    this.name = name;
  }
}

main() {
  var style = new Element.html('''
      <style type="text/css">
        body { margin: 20px; font-family: arial,verdana,helvetica; background: #fff;}
        h1 { font-size: 140%; font-weight:normal; color: #036; border-bottom: 1px solid #ccc; }
        canvas { border: 2px solid #000; margin-bottom: 5px; }
        table { background: #00f; }
        th { font-size: 70%; padding: 0; }
        td { font-size: 70%; padding: 0; }
        pre { float:left; display:block; background: rgb(238,238,238); border: 1px dashed #666; padding: 15px 20px; margin: 0 0 10px 0; }
      </style>
      ''', treeSanitizer: new NullTreeSanitizer());
  document.head.append(style);

  document.body.setInnerHtml('''
      <p>This test exercises a bunch of alpha composition operations on text. The top-left rectangles are the source images and bottom-right rectangles are the destination images.</p>
      <div id="results">
      </div>
      <textarea id="debug"></textarea>
      <div>PASS</div>
      ''', treeSanitizer: new NullTreeSanitizer());

  var compositeTypes = [
    'source-over','source-in','source-out','source-atop',
    'destination-over','destination-in','destination-out','destination-atop',
    'lighter','copy','xor'
  ];
  var inputColors = [
  { 'source': [255, 0, 0, 255], 'destination': [0, 255, 0, 255] },
  { 'source': [255, 0, 0, 255], 'destination': [0, 255, 0, 0] },
  { 'source': [255, 0, 0, 255], 'destination': [0, 255, 0, 1] },
  { 'source': [0, 255, 0, 0], 'destination': [255, 0, 0, 255] },
  { 'source': [0, 255, 0, 1], 'destination': [255, 0, 0, 255] },
  { 'source': [255, 0, 0, 0], 'destination': [0, 255, 0, 255] },
  { 'source': [255, 0, 0, 127], 'destination': [0, 255, 0, 127] },
  { 'source': [255, 0, 0, 255], 'destination': [0, 255, 0, 127] },
  { 'source': [255, 0, 0, 127], 'destination': [0, 255, 0, 255] },
  { 'source': [127, 0, 0, 255], 'destination': [0, 127, 0, 127] },
  { 'source': [127, 0, 0, 127], 'destination': [0, 127, 0, 255] },
  { 'source': [255, 0, 0, 127], 'destination': [255, 0, 0, 63] },
  { 'source': [255, 127, 0, 32], 'destination': [255, 63, 0, 63] },
  { 'source': [255, 0, 0, 191], 'destination': [0, 255, 0, 127] },
  { 'source': [255, 0, 255, 191], 'destination': [0, 255, 255, 127] }
  ];
  var expectedColors = [
    [
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 255, 0, 1] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 255, 0, 1], 'composition': [254, 1, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [255, 0, 0, 127], 'composition': [170, 84, 0, 190], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 0, 127], 'composition': [127, 128, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [127, 0, 0, 255], 'composition': [127, 0, 0, 255], 'destination': [0, 126, 0, 127] },
    { 'source': [126, 0, 0, 127], 'composition': [63, 63, 0, 255], 'destination': [0, 127, 0, 255] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 158], 'destination': [255, 0, 0, 63] },
    { 'source': [255, 127, 0, 32], 'composition': [255, 85, 0, 87], 'destination': [255, 64, 0, 63] },
    { 'source': [255, 0, 0, 191], 'composition': [219, 35, 0, 222], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 255, 191], 'composition': [219, 35, 255, 222], 'destination': [0, 255, 255, 127] }
  ],
    [
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 64], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [128, 0, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [126, 0, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 32], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 127, 0, 8], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 96], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 255, 96], 'destination': [0, 0, 0, 0] }
  ],
    [
    { 'source': [255, 0, 0, 255], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 254], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 255, 0, 1], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 64], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 128], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 127], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [127, 0, 0, 255], 'composition': [127, 0, 0, 128], 'destination': [0, 0, 0, 0] },
    { 'source': [126, 0, 0, 127], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 96], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 127, 0, 32], 'composition': [255, 132, 0, 25], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 191], 'composition': [255, 0, 0, 96], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 255, 191], 'composition': [255, 0, 255, 96], 'destination': [0, 0, 0, 0] }
  ],
    [
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 1], 'destination': [0, 255, 0, 1] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [254, 1, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [126, 126, 0, 127], 'destination': [0, 255, 0, 127] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 127], 'destination': [0, 255, 0, 127] },
    { 'source': [0, 0, 0, 0], 'composition': [127, 128, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [128, 0, 0, 127], 'destination': [0, 126, 0, 127] },
    { 'source': [0, 0, 0, 0], 'composition': [63, 63, 0, 255], 'destination': [0, 127, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 63], 'destination': [255, 0, 0, 63] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 68, 0, 63], 'destination': [255, 64, 0, 63] },
    { 'source': [0, 0, 0, 0], 'composition': [190, 62, 0, 127], 'destination': [0, 255, 0, 127] },
    { 'source': [0, 0, 0, 0], 'composition': [190, 62, 255, 127], 'destination': [0, 255, 255, 127] }
  ],
    [
    { 'source': [255, 0, 0, 255], 'composition': [0, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [254, 1, 0, 255], 'destination': [0, 255, 0, 1] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 255, 0, 1], 'composition': [255, 0, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [255, 0, 0, 127], 'composition': [84, 170, 0, 190], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 0, 255], 'composition': [128, 127, 0, 255], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 0, 127], 'composition': [0, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [127, 0, 0, 255], 'composition': [63, 63, 0, 255], 'destination': [0, 126, 0, 127] },
    { 'source': [126, 0, 0, 127], 'composition': [0, 127, 0, 255], 'destination': [0, 127, 0, 255] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 158], 'destination': [255, 0, 0, 63] },
    { 'source': [255, 127, 0, 32], 'composition': [255, 82, 0, 87], 'destination': [255, 64, 0, 63] },
    { 'source': [255, 0, 0, 191], 'composition': [109, 145, 0, 222], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 255, 191], 'composition': [109, 145, 255, 222], 'destination': [0, 255, 255, 127] }
  ],
    [
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 1], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 1], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 64], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 126, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 128, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 32], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 95, 0, 8], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 96], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 255, 96], 'destination': [0, 0, 0, 0] }
  ],
    [
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 255, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 255, 0, 1] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 254], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 64], 'destination': [0, 255, 0, 127] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 255, 0, 127] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 128], 'destination': [0, 255, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 126, 0, 127] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 127, 0, 128], 'destination': [0, 127, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 32], 'destination': [255, 0, 0, 63] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 63, 0, 56], 'destination': [255, 64, 0, 63] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 32], 'destination': [0, 255, 0, 127] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 255, 32], 'destination': [0, 255, 255, 127] }
  ],
    [
    { 'source': [255, 0, 0, 255], 'composition': [0, 255, 0, 255], 'destination': [0,0,0,0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [254, 1, 0, 255], 'destination': [0,0,0,0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0,0,0,0] },
    { 'source': [0, 255, 0, 1], 'composition': [255, 0, 0, 1], 'destination': [0,0,0,0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0,0,0,0] },
    { 'source': [255, 0, 0, 127], 'composition': [126, 126, 0, 127], 'destination': [0,0,0,0] },
    { 'source': [255, 0, 0, 255], 'composition': [128, 127, 0, 255], 'destination': [0,0,0,0] },
    { 'source': [255, 0, 0, 127], 'composition': [0, 255, 0, 127], 'destination': [0,0,0,0] },
    { 'source': [127, 0, 0, 255], 'composition': [63, 63, 0, 255], 'destination': [0,0,0,0] },
    { 'source': [126, 0, 0, 127], 'composition': [0, 126, 0, 127], 'destination': [0,0,0,0] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 127], 'destination': [0,0,0,0] },
    { 'source': [255, 127, 0, 32], 'composition': [255, 111, 0, 32], 'destination': [0,0,0,0] },
    { 'source': [255, 0, 0, 191], 'composition': [126, 126, 0, 191], 'destination': [0,0,0,0] },
    { 'source': [255, 0, 255, 191], 'composition': [126, 126, 255, 191], 'destination': [0,0,0,0] }
  ],
    [
    { 'source': [255, 0, 0, 255], 'composition': [255, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 1, 0, 255], 'destination': [0, 255, 0, 1] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 255, 0, 1], 'composition': [255, 1, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [255, 0, 0, 127], 'composition': [127, 127, 0, 254], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 127, 0, 255], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 0, 127], 'composition': [127, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [127, 0, 0, 255], 'composition': [127, 63, 0, 255], 'destination': [0, 126, 0, 127] },
    { 'source': [126, 0, 0, 127], 'composition': [63, 127, 0, 255], 'destination': [0, 127, 0, 255] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 190], 'destination': [255, 0, 0, 63] },
    { 'source': [255, 127, 0, 32], 'composition': [255, 85, 0, 95], 'destination': [255, 64, 0, 63] },
    { 'source': [255, 0, 0, 191], 'composition': [191, 127, 0, 255], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 255, 191], 'composition': [191, 127, 255, 255], 'destination': [0, 255, 255, 127] }
  ],
    [
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 255, 0, 1], 'composition': [0, 255, 0, 1], 'destination': [0, 0, 0, 0] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 0, 0, 0], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [127, 0, 0, 255], 'composition': [127, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [126, 0, 0, 127], 'composition': [126, 0, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 127], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 127, 0, 32], 'composition': [255, 127, 0, 32], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 191], 'composition': [255, 0, 0, 191], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 255, 191], 'composition': [255, 0, 255, 191], 'destination': [0, 0, 0, 0] }
  ],
    [
    { 'source': [255, 0, 0, 255], 'composition': [0, 0, 0, 0], 'destination': [0, 255, 0, 255] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 255], 'destination': [0, 0, 0, 0] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 254], 'destination': [0, 255, 0, 1] },
    { 'source': [0, 0, 0, 0], 'composition': [255, 0, 0, 255], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 255, 0, 1], 'composition': [255, 0, 0, 254], 'destination': [255, 0, 0, 255] },
    { 'source': [0, 0, 0, 0], 'composition': [0, 255, 0, 255], 'destination': [0, 255, 0, 255] },
    { 'source': [255, 0, 0, 127], 'composition': [126, 126, 0, 127], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 0, 255], 'composition': [255, 0, 0, 128], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 0, 127], 'composition': [0, 255, 0, 128], 'destination': [0, 255, 0, 255] },
    { 'source': [127, 0, 0, 255], 'composition': [127, 0, 0, 128], 'destination': [0, 126, 0, 127] },
    { 'source': [126, 0, 0, 127], 'composition': [0, 125, 0, 128], 'destination': [0, 127, 0, 255] },
    { 'source': [255, 0, 0, 127], 'composition': [255, 0, 0, 127], 'destination': [255, 0, 0, 63] },
    { 'source': [255, 127, 0, 32], 'composition': [255, 83, 0, 79], 'destination': [255, 64, 0, 63] },
    { 'source': [255, 0, 0, 191], 'composition': [190, 62, 0, 127], 'destination': [0, 255, 0, 127] },
    { 'source': [255, 0, 255, 191], 'composition': [190, 62, 255, 127], 'destination': [0, 255, 255, 127] }
  ]
  ];

  // Compare two colors with a few margin.
  isDifferentColor(actualColor, expectedColor)
  {
    var actualAlpha = actualColor[3];
    var expectedAlpha = expectedColor[3];
    if (abs(actualAlpha - expectedAlpha) > 3) return true;
    // For the value of RGB, we compare the values the users actually see.
    if (abs(actualColor[0] * actualAlpha / 256 - expectedColor[0] * expectedAlpha / 256) > 3) return true;
    if (abs(actualColor[1] * actualAlpha / 256 - expectedColor[1] * expectedAlpha / 256) > 3) return true;
    if (abs(actualColor[2] * actualAlpha / 256 - expectedColor[2] * expectedAlpha / 256) > 3) return true;
    return false;
  }

  getRGBAString(a)
  {
    return "rgba(" + [a[0], a[1], a[2], a[3] / 255.0].join(",") + ")";
  }

  drawTable(drawPolicy)
  {
    var tableElement = document.createElement("table");

    // Create a header for source color.
    var trElement = document.createElement("tr");
    tableElement.append(trElement);
    trElement.append(document.createElement("th"));
    for (var column = 0; column < inputColors.length; column++) {
      var inputColor = inputColors[column];
      var thElement = document.createElement("th");
      thElement.setAttribute("colspan", "2");
      thElement.text = "src " + inputColor['source'].join(", ");
      trElement.append(thElement);
    }

    // Create a header for destination color.
    trElement = document.createElement("tr");
    tableElement.append(trElement);
    trElement.append(document.createElement("th"));
    for (var column = 0; column < inputColors.length; column++) {
      var inputColor = inputColors[column];
      var thElement = document.createElement("th");
      thElement.setAttribute("colspan", "2");
      thElement.text = "dst " + inputColor['destination'].join(", ");
      trElement.append(thElement);
    }

    var resultsElement = document.getElementById("results");
    var titleElement = document.createElement("h1");
    titleElement.text = "Tests for " + drawPolicy.name;
    resultsElement.append(titleElement);
    resultsElement.append(tableElement);

    for (var row = 0; row < compositeTypes.length; row++){
      var type = compositeTypes[row];

      var trCanvasElement = document.createElement("tr");
      var thElement = document.createElement("th");
      thElement.setAttribute("rowspan", "2");
      thElement.text = type;
      trCanvasElement.append(thElement);
      var trMessageElement = document.createElement("tr");
      tableElement.append(trCanvasElement);
      tableElement.append(trMessageElement);

      for (var column = 0; column < inputColors.length; column++) {
        var test = '$type-$column';
        var inputColor = inputColors[column];
        var expectedColor = expectedColors[row][column];

        // Create canvas element for actual color.
        var actualCanvasElement = document.createElement("canvas");
        actualCanvasElement.setAttribute("width", "25");
        actualCanvasElement.setAttribute("height", "25");
        var tdElement = document.createElement("td");
        tdElement.append(actualCanvasElement);
        trCanvasElement.append(tdElement);

        // Create canvas element for expected color.
        var expectedCanvasElement = document.createElement("canvas");
        expectedCanvasElement.setAttribute("width", "25");
        expectedCanvasElement.setAttribute("height", "25");
        tdElement = document.createElement("td");
        tdElement.append(expectedCanvasElement);
        trCanvasElement.append(tdElement);

        // Create div element for pass/fail messages.
        var messageElement = document.createElement("div");
        tdElement = document.createElement("td");
        tdElement.setAttribute("colspan", "2");
        tdElement.append(messageElement);
        trMessageElement.append(tdElement);

        var ctx = expectedCanvasElement.getContext("2d");
        ctx.lineWidth = 3;
        // Draw expected image.
        ctx.globalCompositeOperation = "copy";
        ctx.fillStyle = getRGBAString(expectedColor['destination']);
        ctx.strokeStyle = getRGBAString(expectedColor['destination']);
        drawPolicy.drawDestination(ctx);
        ctx.fillStyle = getRGBAString(expectedColor['source']);
        ctx.strokeStyle = getRGBAString(expectedColor['source']);
        drawPolicy.drawSource(ctx);
        ctx.fillStyle = getRGBAString(expectedColor['composition']);
        ctx.strokeStyle = getRGBAString(expectedColor['composition']);
        drawPolicy.drawComposition(ctx);

        ctx = actualCanvasElement.getContext("2d");
        ctx.lineWidth = 3;

        // Draw destination rectangle.
        ctx.globalCompositeOperation = "copy";
        ctx.fillStyle = getRGBAString(inputColor['destination']);
        ctx.strokeStyle = getRGBAString(inputColor['destination']);
        drawPolicy.drawDestination(ctx);

        // Draw source rectangle.
        ctx.globalCompositeOperation = type;
        ctx.fillStyle = getRGBAString(inputColor['source']);
        ctx.strokeStyle = getRGBAString(inputColor['source']);
        drawPolicy.drawSource(ctx);

        // Let's check if the results are expected or not.
        var errorSuffix = ", composite type: $type, source: ${inputColor['source']}, destination: ${inputColor['destination']}<br>";

        var results = "";
        // Note that (2, 3) may be affected by anti-alias.
        var img = ctx.getImageData(3, 4, 1, 1).data;
        var actualColor = [img[0], img[1], img[2], img[3]];
        if (isDifferentColor(actualColor, expectedColor['source'])) {
          results += "Unexpected source! expected: ${expectedColor['source']} actual: $actualColor $errorSuffix";
        }
        // Note that (24, 24) may be affected by anti-alias.
        img = ctx.getImageData(23, 23, 1, 1).data;
        actualColor = [img[0], img[1], img[2], img[3]];
        if (isDifferentColor(actualColor, expectedColor['destination'])) {
          results += "Unexpected destination! expected: ${expectedColor['destination']} actual: $actualColor $errorSuffix";
        }
        img = ctx.getImageData(11, 12, 1, 1).data;
        actualColor = [img[0], img[1], img[2], img[3]];
        if (isDifferentColor(actualColor, expectedColor['composition'])) {
          results += "Unexpected composition! expected: ${expectedColor['composition']} actual: $actualColor $errorSuffix";
        }

        if (results == "") {
          messageElement.style.backgroundColor = "green";
          messageElement.innerHtml = results = "OK";
        } else {
          messageElement.style.backgroundColor = "red";
          messageElement.innerHtml = results;
          testFailed(results);
        }

        // Dump colors into text area for debugging purpose.
        var debugText = document.getElementById("debug");
        img = ctx.getImageData(0, 0, 1, 1).data;
        debugText.value += img.join(",") + "\n";
        img = ctx.getImageData(12, 12, 1, 1).data;
        debugText.value += img.join(",") + "\n";
        img = ctx.getImageData(24, 24, 1, 1).data;
        debugText.value += img.join(",") + "\n";
      }
    }
  }

  var useFillText = new DrawPolicy(
      drawSource: (ctx) {
        ctx.font="bold 20px arial";
        ctx.fillText("B", 0, 18);
      },

      drawDestination: (ctx) {
        ctx.fillRect(5, 5, 20, 20);
      },

      drawComposition: (ctx) {
        ctx.fillRect(5, 5, 15, 15);
      },

      name: "fill text");

  var useStrokeText = new DrawPolicy(
      drawSource: (ctx) {
        ctx.font="bold 20px arial";
        ctx.strokeText("B", 0, 18);
      },

      drawDestination: (ctx) {
        ctx.fillRect(5, 5, 20, 20);
      },

      drawComposition: (ctx) {
        ctx.fillRect(5, 5, 15, 15);
      },

      name: "stroke text");

  draw()
  {
    drawTable(useFillText);
    drawTable(useStrokeText);
    asyncEnd();
  }

  asyncStart();
  window.onLoad.listen((_) {
    draw();
  });
}

